package dsf.eshop.goods.entity;

import dsf.eshop.goods.entity.vo.Image;
import lombok.Data;
import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption;

import javax.persistence.*;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

@Entity
@Table
@Data
public class Goods implements Serializable {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;
    private String description;
    private int price;
    private int status;
    private int num;

    @Column(name = "create_time")
    private Date createTime;


    /**
     * 单向多对一
     * mappedBy 表示 一对多的映射关系由 “多”的一方维护
     *  就是说在image表中每一行存image及对应的goods_id
     *  mappedy的值应该是 当前表 goods与image的连表条件对应的Entity属性
     *  连表条件是 goods.id = goods_image.goods_id，
     *  所以这里应该是goods_image表中goods_id对应的属性goodsId
     *
     * 不加mappedy表示 用一张新表来维护
     */
    @Transient
    @OneToMany(mappedBy = "goodsId")
    @LazyCollection(LazyCollectionOption.FALSE)
    private List<Image> images;

    /*
     * 双向一对多
     *   我下面是通过一张新表goods_and_image实现的
     *      连接条件：goods.id = goods_and_image.goods_id
     *          and goods_and_image.image_id = goods_image.image_id
     *   也可以不新建表 不过需要 goods_image 与 goods 连接之后 再与 goods_image 连接
     *      连接条件：goods.id = goods_image.goods_id
     *          and goods_image.goods_id = goods.id
     *
     * 我这是新建一张goods_and_image的实现 代码如下
     *   @JoinTable
     *       name属性表示连表的目标表
     *          当前表需要先join goods_and_type 再join goods_type, name 值就是goods_and_type
     *       joinCloumns 它的值是一个列表 可以是多个@JoinColumn
     *          @JoinColumn 每个@JoinColumn是一个连表条件
     *              name goods_and_image表中的与当前表连接的字段 goods_id
     *              referencedColumnName 当前表中参与连接的字段 id
     *              上面两个值都需要是数据表中的字段 JPA可以自动转换驼峰式命名：假如name写成goodsId也可以，
     *                  因为JPA会转换成goods_id
     *       inverseJoinColumns 和 joinCloumns 差不多
     *          @JoinColumn
     *              name 还是中间那个表（goods_and_image）中的字段
     *                  不过joinCloumns中的name是当前表与goods_and_image的连接的字段
     *                  但 inverseJoinColumns 中的 name 是
     *                  goods_and_image 中与另一边的表goods_image 连接的字段 image_id
     *              referencedColumnName 是 另一边的表(goods_image)被连接的字段 id
     * */
//    @OneToMany
//    @LazyCollection(LazyCollectionOption.FALSE)
//    @JoinTable(name = "goods_and_image",
//            joinColumns = {@JoinColumn(name = "goodsId", referencedColumnName = "id")},
//            inverseJoinColumns = {@JoinColumn(name = "image_id", referencedColumnName = "id")})
//    private List<Image> images;


    /*
    * 多对多
    * 其实单向多对多和上面双向OneToMany的写法是差不多的，
    *   只需要在单向多对多的主动的一边注解@ManyToMany
    *   具体的值根据上面讲的写就好了
    * 因为只从一边来讲 OneToMany 和 ManyToMany 是一样的
    *
    * 而双向的多对多 其实就是在另一边也加上ManyToMany注解
    *   只不过 另一边的注解 以另一边为第一人称。。。。
    *   （我表述能力可能有点问题 但我也想不到更好的表述了）
    * */
    @Transient
    @ManyToMany
    @LazyCollection(LazyCollectionOption.FALSE)
    @JoinTable(name = "goods_and_type",
            joinColumns = {@JoinColumn(name = "goods_id")},
            inverseJoinColumns = {@JoinColumn(referencedColumnName = "type_id", name = "types_id")})
    private List<GoodsType> goodsTypes;


    public void postImages(List<Image> images) {

        if(!(images instanceof LinkedList)) {
            this.images = toLinkedList(images);
        }
        this.images.addAll(images);
    }

    /*
    * 这个场景可以直接选择遍历
    * 假如是遍历的对象比较多 可以选择先排序(快排)，再进行比较
    * */
    public void deleteImages(List<Integer> ids) {

        if(!(images instanceof LinkedList)) {
            images = toLinkedList(images);
        }
        for (Image next : images) {
            if (ids.contains(next.getGoodsId())) {
                images.remove(next);
            }
        }
    }

    /*
    * 假如add的type是当前goods的类别中的某个
    *   或是当前所属类别中某个类别的父类别 (也就是当前类别是子类别)
    *   拒绝插入
    * TODO  if there no types you want add , can add any types defined by yourself
    * TODO  when add goods_types , want add new types, should goto the add_types' page? no!
    * */
    public void putTypes(List<GoodsType> goodsTypes) {

        for (GoodsType goodsType : goodsTypes) {
            if(this.goodsTypes.contains(goodsType)) {
                goodsTypes.remove(goodsType);
            }
        }

        for (GoodsType goodsType : this.goodsTypes) {
            boolean flag = true;
            GoodsType add = null;
            Iterator<GoodsType> iterator = goodsTypes.iterator();
            while(iterator.hasNext()) {

                add = iterator.next();
                if(goodsType.isPosterity(add.getId())) {
                    flag = false;
                    break;
                }
            }
            if(flag) {
                this.goodsTypes.add(add);
            }
        }
    }

    /*
    * 当前goodsTypes中有id对应Type就移除
    * 没有就不操作
    * */
    public void deleteTypes(List<Integer> ids) {

        for (GoodsType goodsType : goodsTypes) {
            if(ids.contains(goodsType.getId())) {
                goodsTypes.remove(goodsType);
            }
        }
    }

    protected LinkedList toLinkedList(List list) {
        if(!(list instanceof LinkedList)) {


            /*
            * TODO 将list转成LinkedList 且保留泛型
            * */
//            Class<? extends List> aClass = list.getClass();
//            Type[] interfaces = aClass.getGenericInterfaces();
//            Type type = null;
//            for (Type anInterface : interfaces) {
//                type = ((ParameterizedType) anInterface).getActualTypeArguments()[0];
//                if(null != type) {
//                    break;
//                }
//            }
//            if(type != null) {
//                Class<?> aClass1 = Class.forName(type.getTypeName());
//                return new LinkedList<>(list);
//            }
        }
        return (LinkedList) list;
    }
}



